﻿#region File and License Information
/*
<File>
	<Copyright>Copyright © 2007, Daniel Vaughan. All rights reserved.</Copyright>
	<License see="prj:///Documentation/License.txt"/>
	<Owner Name="Daniel Vaughan" Email="dbvaughan@gmail.com"/>
	<CreationDate>2008-11-03 23:36:57Z</CreationDate>
	<LastSubmissionDate>$Date: $</LastSubmissionDate>
	<Version>$Revision: $</Version>
</File>
*/
#endregion

using System;
using System.Threading;
using System.Windows;
using System.Windows.Threading;

using DanielVaughan.Concurrency;

using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DanielVaughan.Tests
{
	/// <summary>
	/// Unit tests for <see cref="UISynchronizationContext"/>.
	/// </summary>
	[TestClass]
	public class UISynchronizationContextTest : SilverlightTest
	{
		const int timoutMS = 20000;
		static ISynchronizationContext context;
		static Dispatcher dispatcher;

		[ClassInitialize]
		public static void ClassInitialize()
		{
			dispatcher = Application.Current.RootVisual.Dispatcher;
			context = UISynchronizationContext.Instance;
			context.Initialize(dispatcher);
		}

		[TestMethod]
		[Asynchronous]
		public void InvokeRequiredShouldReturnTrueIfCalledFromNonUIThread()
		{
			ThreadPool.QueueUserWorkItem(delegate
			                             	{
												Assert.IsTrue(context.InvokeRequired);
												TestComplete();
			                             	});
		}

		[TestMethod]
		public void InvokeRequiredShouldReturnFalseIfCalledFromUIThread()
		{
			Assert.IsFalse(context.InvokeRequired);
		}

		#region Synchronous
		[TestMethod]
		[Asynchronous]
		public void InvokeSynchronouslyShouldPerformActionSynchronouslyFromNonUIThread()
		{
			ThreadPool.QueueUserWorkItem(delegate
			                             	{
			                             		CallInvokeSynchronouslyWithAction();
												TestComplete();
			                             	});
		}

		[TestMethod]
		public void InvokeSynchronouslyShouldPerformActionSynchronouslyFromUIThread()
		{
			CallInvokeSynchronouslyWithAction();
		}

		void CallInvokeSynchronouslyWithAction()
		{
			var resetEvent = new ManualResetEvent(false);
			bool completedSynchronously = false;
			context.InvokeSynchronously(delegate
			{
				try
				{
					Thread.Sleep(5000);
					completedSynchronously = true;
					/* If this is not performed on the UI thread 
					 * it will throw an UnauthorizedAccessException. */
					Application.Current.RootVisual.CheckAccess();
				}
				finally
				{
					resetEvent.Set();
				}
			});
			
			if (!completedSynchronously)
			{
				throw new AssertFailedException("Method did not complete synchronously.");
			}

			resetEvent.WaitOne(timoutMS);
		}

		[TestMethod]
		[Asynchronous]
		public void ContextShouldInvokeActionSynchronouslyWithSendPostCallbackFromNonUIThread()
		{
			ThreadPool.QueueUserWorkItem(delegate
			{
				InvokeSynchronouslyWithSendPostCallback();
				TestComplete();
			});
		}

		[TestMethod]
		public void ContextShouldInvokeActionSynchronouslyWithSendPostCallbackFromUIThread()
		{
			InvokeSynchronouslyWithSendPostCallback();
		}
        
		void InvokeSynchronouslyWithSendPostCallback()
		{
			var resetEvent = new ManualResetEvent(false);
			bool completedSynchronously = false;
			context.InvokeSynchronously(delegate
			{
				try
				{
					Thread.Sleep(5000);
					completedSynchronously = true;
					/* If this is not performed on the UI thread 
					 * it will throw an UnauthorizedAccessException. */
					Application.Current.RootVisual.CheckAccess();
				}
				finally
				{
					resetEvent.Set();
				}
			}, new object());

			if (!completedSynchronously)
			{
				throw new AssertFailedException("Method did not complete synchronously.");
			}

			resetEvent.WaitOne(timoutMS);
		}
		#endregion

		#region Asynchonous
		[TestMethod]
		[Asynchronous]
		public void InvokeAsynchronouslyShouldCallActionAsynchronouslyFromNonUIThread()
		{
			ThreadPool.QueueUserWorkItem(delegate
			                             	{
			                             		CallInvokeAsynchronouslyWithAction();
												TestComplete();
			                             	});
		}

		[TestMethod]
		public void InvokeAsynchronouslyShouldCallActionAsynchronouslyFromUIThread()
		{
			CallInvokeAsynchronouslyWithAction();
		}

		void CallInvokeAsynchronouslyWithAction()
		{
			var resetEvent = new ManualResetEvent(false);
			context.InvokeAsynchronously(delegate
			{
				try
				{
					/* If this is not performed on the UI thread 
					 * it will throw an UnauthorizedAccessException. */
					Application.Current.RootVisual.CheckAccess();
				}
				finally
				{
					resetEvent.Set();
				}
			});
			resetEvent.WaitOne(timoutMS);
		}

		[TestMethod]
		[Asynchronous]
		public void InvokeAsynchronouslyShouldCallSendOrPostCallbackAsynchronouslyFromNonUIThread()
		{
			ThreadPool.QueueUserWorkItem(delegate
			                             	{
												CallInvokeAsynchronouslyWithSendPostCallback();
			                             		TestComplete();
			                             	});
		}

		[TestMethod]
		public void InvokeAsynchronouslyShouldCallSendOrPostCallbackAsynchronouslyFromUIThread()
		{
			CallInvokeAsynchronouslyWithSendPostCallback();
		}

		void CallInvokeAsynchronouslyWithSendPostCallback()
		{
			var resetEvent = new ManualResetEvent(false);
			context.InvokeAsynchronously(delegate
			{
				try
				{
					/* If this is not performed on the UI thread 
					 * it will throw an UnauthorizedAccessException. */
					Application.Current.RootVisual.CheckAccess();
				}
				finally
				{
					resetEvent.Set();
				}
			}, new object());
			resetEvent.WaitOne(timoutMS);
		}
		#endregion
	}
}
